﻿using CSPointer;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
using UnityMidi;
public class GameStart : MonoBehaviour
{
    //public RenderTexture rTex;
    public int testpos = 805192;
    public int testvalue = 630;
    public int nMax = 40000;
    public uint memSize = 2; 
    public static GameStart Ins;
    public int TestThumbIndex = 1;
    public  bool isOpenLog;
    public bool isOpenPrint;
    public bool NoShow;

    public bool isGbaArm;
    public bool isArmv5;
    public bool UesExt;
    public bool UesTimer;
    public string MrpName;
    public string Mrp2Name;
    public int LogNumMax=20;
    public static bool isPrint;

    public static bool isLog;
    public Text GameName;
    public Texture2D texture;
    public RawImage rawImage;
    public int width;
    public int height;
    public int WH;
    public static int FlushMode;
    public static string MrpFileName;
    public Dictionary<KeyCode, AnonymousEnum9> keys=new Dictionary<KeyCode, AnonymousEnum9>();
    public int CFlushMode;
    public uint[] registers = new uint[16]; //寄存器列表
    public List< byte> stack=new List<byte>();
    public uint into = 8;
    public float RunTime = 1000;
    public AudioSource AudioSource;
    public int AudioSourceLength=10000;
    private float[] AudioData;
   // public LinkedList<Timer> timers = new LinkedList<Timer>();
    public List<Timer> timers = new List<Timer>();
    public MidiPlayer MidiPlayer;
    // Start is called before the first frame update
    private void Awake()
    {
       
        widthH = Screen.width / 2;
        heightH = Screen.height / 2;
        isPrint = isOpenPrint;
        
    }

    private Color[] _displayTable = new Color[240 * 320];
    private Color[] _colorLookupTable = new Color[65536];
    private Color32[] _displayTable32 = new Color32[240 * 320];
    //private Color32[] _colorLookupTable32 = new Color32[65536];
    private Dictionary<ushort, Color32> _colorLookupTable32Dic = new Dictionary<ushort, Color32>();
    unsafe void Start()
    {
        //stack = new byte[DefineConstants.VM_STACK_SIZE];
        Ins = this;
        FlushMode = CFlushMode;
        MrpFileName = MrpName;
        OptimeGBA .Arm.Armv5=isArmv5;
        for (int i = 0; i < 32768; i++)
        {
            float rr = (float)(i & 0x001F);
            float r = (float)(i & 0x001F) / (float)0x001F;
            float g = (float)(i & 0x03E0) / (float)0x03E0;
            float b = (float)(i & 0x7C00) / (float)0x7C00;

            _colorLookupTable[i] = new Color(r,g,b,1.0f);
            _colorLookupTable[i + 32768] = _colorLookupTable[i];
            Color32 color32 = _colorLookupTable[i];
            //_colorLookupTable32[i] = _colorLookupTable[i];
            //_colorLookupTable32[i + 32768] = _colorLookupTable[i];
            ushort color = GetColor32ToUshort(color32.r, color32.g, color32.b);
            if (!_colorLookupTable32Dic.ContainsKey(color))
            _colorLookupTable32Dic.Add(color, _colorLookupTable[i]);
        }
        GlobalMembers.logMax = LogNumMax;
        path = Application.dataPath + @"\..\" + MrpName + "_Re.txt";
        //if (isGbaArm)
        //{
        //    SeveFileStream();
        //}
        //else
        //{
        //    RandFileStream();
        //}
        timers.Add(new Timer());
        GameName.text = MrpFileName;
        GlobalMembers.test_mrp();
        InitTexture();
        if (Application.platform ==RuntimePlatform.PSP2)
        {
            keys.Add(KeyCode.Joystick1Button11, AnonymousEnum9.MR_KEY_LEFT);
            keys.Add(KeyCode.Joystick1Button9, AnonymousEnum9.MR_KEY_RIGHT);
            keys.Add(KeyCode.Joystick1Button8, AnonymousEnum9.MR_KEY_UP);
            keys.Add(KeyCode.Joystick1Button10, AnonymousEnum9.MR_KEY_DOWN);
            keys.Add(KeyCode.Joystick1Button1, AnonymousEnum9.MR_KEY_SELECT);
            keys.Add(KeyCode.Joystick1Button2, AnonymousEnum9.MR_KEY_STAR);
            keys.Add(KeyCode.Joystick1Button3, AnonymousEnum9.MR_KEY_POUND);


            keys.Add(KeyCode.Joystick1Button4, AnonymousEnum9.MR_KEY_SOFTLEFT);
            keys.Add(KeyCode.Joystick1Button5, AnonymousEnum9.MR_KEY_SOFTRIGHT);
        }
        else
        {
            keys.Add(KeyCode.A, AnonymousEnum9.MR_KEY_LEFT);
            keys.Add(KeyCode.D, AnonymousEnum9.MR_KEY_RIGHT);
            keys.Add(KeyCode.W, AnonymousEnum9.MR_KEY_UP);
            keys.Add(KeyCode.S, AnonymousEnum9.MR_KEY_DOWN);
            keys.Add(KeyCode.H, AnonymousEnum9.MR_KEY_SELECT);
            keys.Add(KeyCode.G, AnonymousEnum9.MR_KEY_STAR);
            keys.Add(KeyCode.F, AnonymousEnum9.MR_KEY_POUND);

            keys.Add(KeyCode.Q, AnonymousEnum9.MR_KEY_SOFTLEFT);
            keys.Add(KeyCode.E, AnonymousEnum9.MR_KEY_SOFTRIGHT);
        }

      


        if (GlobalMembers.reader!=null)
        GameName.text = GlobalMembers.reader.info.showName;
        else
            GameName.text = "ext";
    }

    public unsafe void InitTexture()
    {
        if(texture==null)
        {
            //texture = new Texture2D(rawImage.texture.width, rawImage.texture.height, TextureFormat.BGRA32, false);
            width = (int)GlobalMembers.vm.mem->mr_state.sysinfo.screen_width;
            height = (int)GlobalMembers.vm.mem->mr_state.sysinfo.screen_height;
            WH = width * height;
            texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
            //texture.LoadRawTextureData((IntPtr)p, DefineConstants.VM_MAX_PIXELS);

            rawImage.texture = texture;
            rawImage.SetNativeSize();
            //SetTexture(GlobalMembers.vm, null);
            isInitTexture = true;
        }

    }

    public unsafe void PlayAudio(uint index=1,bool loop=true)
    {
        if (GlobalMembers.vm == null) return;
        mr_sound_t sound = default(mr_sound_t);
        switch (index)
        {
            case 0:
                sound = GlobalMembers.vm.mem->mr_state.mr_sound;
                break;
            case 1:
                sound = GlobalMembers.vm.mem->mr_state.mr_sound2;
                break;
            case 2:
                sound = GlobalMembers.vm.mem->mr_state.mr_sound3;
                break;
            case 3:
                sound = GlobalMembers.vm.mem->mr_state.mr_sound4;
                break;
            case 4:
                sound = GlobalMembers.vm.mem->mr_state.mr_sound5;
                break;
            default:
                break;
        }
        //if (GlobalMembers.vm.mem->mr_state.mr_soundOn != 0)
        if (!IsLoaded)
        {
            if (sound.buflen>0)
            {
                byte[] data = new byte[sound.buflen];
                uint len = sound.buflen;
                for (int i = 0; i < len;i++)
                {
                    data[i ] = *(GlobalMembers.vm.cpu.mem.bufferP + i + sound.p);
                }
                //IsLoaded = true;
                MidiPlayer.LoadMidi(data);
                MidiPlayer.Play(loop);
            }
            else
            {
                //if (MidiPlayer.IsPlay)
                //    MidiPlayer.Stop();
            }
        }
       
    }
    public bool IsLoaded;

    static bool isExit=false;

    public static void Exit()
    {
        isExit = true;
        throw new NotImplementedException();
    }

    public void ExitGame()
    {
        Application.LoadLevel(Application.loadedLevelName);
    }

    public int Id;
    public int timeId;

    public int AddTimer(float time)
    {
        Timer t = new Timer();
        if (Id > 1000)
            Id = 0;
        t.id = Id++;
        t.time = time;
        //timers.AddLast(new LinkedListNode<Timer>(t));
        if (t.time > 0)
            timers.Add((t));
        //timers[0] = t;
        timeId = Id;
        return Id;
    }

    public void RemoveTimer(int id)
    {
        Timer tT = null;
        foreach (Timer t in timers)
        {
            if (t.id ==id)
            {
                tT = t;
                break;
            }
        }
                timers.Remove(tT);
    }

    public int StartTimer(uint time)
    {
       
        if(!UesTimer) return 0;
        timeId = AddTimer(time / RunTime);
        //if (timers .Count<= 0)
        //{
            

        //}
        //else
        //{
        //    timers[0].time = time / 1000f;
           

        //} isStartTime = true;
        //if (0==timeId)
        //{
        //    timeId = AddTimer(time/1000f);
        //}
        //else
        //{
        //    RemoveTimer(timeId);
        //    timeId = AddTimer(time/1000f);
        //}
        //StartCoroutine(events(time / 1000f));
        //if (Id == 0)
        //{
        //    Id++;
        //    StartCoroutine(events(time / 1000));
        //}
        //else
        //{
        //    StopAllCoroutines();
        //    Id++;
        //    StartCoroutine(events(time / 1000));
        //}
        // print(Id);
        return 0;
    }

    //public R getR(uint time)
    //{
    //    R r = new R();
    //    r.time = time;
    //    r.e = () => { events(time / 1000); };
    //    return r;
    //}

    public void StopTimer()
    {
        timers.Clear();
        //timers[0].time = 0;
        if (!UesTimer) return;
        //if (timeId!=0)
        //{
        //    RemoveTimer(timeId);
        //    timeId = 0;
        //}
        //StopAllCoroutines();
        //if (Id != 0)
        //{
        //    //GlobalMembers.mr_stop(GlobalMembers.vm, (uint) AnonymousEnum10.MR_EXIT_EVENT);
        //    StopAllCoroutines();
        //    Id = 0;
        //}
        //timers.Clear();
        //isStartTime = false;
    }

    public void getTime()
    {

    }

    public IEnumerator events(float time)
    {
        yield return new WaitForSeconds(time);
        GlobalMembers.mr_timer(GlobalMembers.vm);
    }
    private bool isInitTexture;
    public unsafe void SetTexture(vm_info_st vm, ushort* bmp, int X=0, int Y=0, int w=240, int h=320)
    {
        if (NoShow || texture == null) return;
        if (!isInitTexture)
          InitTexture();
        //RefreshTexture(vm);
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                int i = y * width + x;
                ushort index = vm.mem->video[(height-y-1)*width+x];
                
                //if (_colorLookupTable32Dic.ContainsKey(index))
                //    _displayTable32[i] = _colorLookupTable32Dic[index]; // the real question is can a uint array be converted to Color32 fast?
                //else
                    _displayTable32[i] = GetColor(index);
            }
        }
        texture.SetPixels32(_displayTable32);
        texture.Apply();
    }

    private unsafe void RefreshTexture(vm_info_st vm)
    {
        if (NoShow || texture == null) return;
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                {
                    texture.SetPixel(x, height - y, GetColor(vm.mem->video[y * width + x]));
                }
            }
        }

        //for (int i = WH-1; i >=0; --i)
        //{
        //    ushort index = vm.mem->video[i];
        //    if (_colorLookupTable32Dic.ContainsKey(index))
        //        _displayTable32[i] = _colorLookupTable32Dic[index]; // the real question is can a uint array be converted to Color32 fast?
        //    else
        //        _displayTable32[i] = GetColor(index);
        //}

        //texture.SetPixels32(_displayTable32);
        texture.Apply();
    }

    public Color GetColor(ushort v)
    {
        if (v == 0) return Color.black;
        byte r =(byte)( (v >> 11) << 3);
        byte g = (byte)((v>>5)<<2);
        byte b = (byte)((v) << 3);
        if (r == 0 && g == 0 && b == 0) return Color.clear;
        Color32 color = new Color32(r,g,b,255);
        return color;
    }

    //public ushort GetColor32ToUshort(float r,float g,float b)
    //{
    //    int R = (int)Mathf.Ceil(r * 255);
    //    int G = (int)Mathf.Ceil(g * 255);
    //    int B = (int)Mathf.Ceil(b * 255);
    //    ushort color=(ushort)(((uint)(R >> 3) << 11) + ((uint)((G ) >> 2) << 5) + ((uint)((B) >> 3)));
    //    return color;
    //}

    public ushort GetColor32ToUshort(float r, float g, float b)
    {
        int R = (int)Mathf.Ceil(r );
        int G = (int)Mathf.Ceil(g );
        int B = (int)Mathf.Ceil(b );
        ushort color = (ushort)(((uint)(R >> 3) << 11) + ((uint)((G) >> 2) << 5) + ((uint)((B) >> 3)));
        return color;
    }

    public bool isUpdate;
    public bool isStartTime;
    public bool isEnd;
    // Update is called once per frame
    void Update()
    {
        if (isExit) return;
        isPrint = isOpenPrint;

        //Timer removet = null;
        if (timers.Count>0)
        {
            if(!isUpdate )
            {
                for (int i = 0; i < timers.Count; i++)
                {
                    Timer t = timers[i];
                    if (t.time <= 0)
                    {
                        //isEnd = false;
                        GlobalMembers.mr_timer(GlobalMembers.vm);

                        //timeId = 0;
                        //removet = t;
                    }
                    else
                    {
                        t.time -= Time.deltaTime;
                    }
                }
            }
           
        }
       

        //if(removet!=null)
        //{
        //    timers.Remove(removet);

        //}

        //Graphics.Blit(texture, rTex);
        if (isUpdate)
        {
            for (int i = 0; i < Cycle; i++)
            {
                GlobalMembers.UpData();

                if (GlobalMembers.vm.cpu.registers[DefineConstants.r_pc] < 16)
                {
                    isUpdate = false;
                    break;
                }
            }
        }
        else
        {
            ExeEvent();
        }
        //SetTexture();

        isLog = isOpenLog;
        //ShowStack();
        for (int i = 0; i < registers.Length; i++)
        {
            if (i < registers.Length)
                registers[i] = GlobalMembers.vm.cpu.registers[i];
        }
        //PlayAudio(AudioIndex);
        
        //MOUSEEvent(Input.mousePosition);
    }

    public uint AudioIndex=0;

    public unsafe void ShowStack()
    {
        for (int i = 0; i < DefineConstants.VM_STACK_SIZE- GlobalMembers.vm.cpu.registers[13]+20; i++)
        {
            if(stack.Count<=i)
            {
                stack.Add(*(GlobalMembers.vm.mem->stack + DefineConstants.VM_STACK_SIZE - i));
            }
            else
            {

            stack[i] =*( GlobalMembers.vm.mem->stack+ DefineConstants.VM_STACK_SIZE-i);
            }
           
        }
    }

    public uint Cycle = 20000;

    private KeyCode currentkey;
    void OnGUI()
    {
        if (!isUpdate)
        {
            //this.transform.Find("Text").GetComponent<Text>().color = new Color(1, 1, 0);   //设置键值显示的颜色，设置状态为黄色，非设置状态为白色
            if (Input.anyKeyDown) //当输入任意按键时 往下执行
            {
                Event fee = Event.current; //获取GUI正在处理的事件。
                if (fee.isKey) //当为按键事件
                {
                    currentkey = fee.keyCode;//获取按键
                    if (currentkey.ToString() != "None") //当按键不为空时 设置
                    {
                      
                        KeyEvent(currentkey);

                        if (Input.GetKeyDown(KeyCode.O))
                            GlobalMembers.mr_timer(GlobalMembers.vm);
                    }
                }
            }
        }
    }


    private void KeyEvent(KeyCode key)
    {
        if (!keys.ContainsKey(key)) return;

        if (Input.GetKeyDown(key))
        {
            GlobalMembers.mr_event(GlobalMembers.vm, (uint)AnonymousEnum10.MR_KEY_PRESS, (uint)keys[key], 0);
        }
        else if (Input.GetKeyUp(key))
        {
            GlobalMembers.mr_event(GlobalMembers.vm, (uint)AnonymousEnum10.MR_KEY_RELEASE, (uint)keys[key], 0);
        }
    }

    private float widthH;
    private float heightH;

    private void MOUSEEvent(Vector2 pos)
    {
        if (isUpdate)
        {
            return;
        }
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 p = MouseButtonEvent(pos);
            GlobalMembers.mr_event(GlobalMembers.vm, (uint)AnonymousEnum10.MR_MOUSE_DOWN,(uint)p.x, (uint)p.y);

        }
        if (Input.GetMouseButtonUp(0))
        {
            Vector2 p = MouseButtonEvent(pos);

            GlobalMembers.mr_event(GlobalMembers.vm, (uint)AnonymousEnum10.MR_MOUSE_UP, (uint)p.x, (uint)p.y);
        }
    }

    private ActionEvent actionEvent;

    public void MouseOnClickEvent(Vector2 pos,bool isUp)
    {
        //if (isUpdate)
        //{
        //    return;
        //}
        Vector2 point = new Vector2(pos.x * width, (1 - pos.y) * height);

        GlobalMembers.mr_event(GlobalMembers.vm, isUp?(uint)AnonymousEnum10.MR_MOUSE_UP: (uint)AnonymousEnum10.MR_MOUSE_DOWN
            , (uint)point.x, (uint)point.y);
       
    }

    public void AddEvent(bool isKey, Vector2 pos, bool isUp)
    {
        if(actionEvent==null)
        {
            actionEvent = new ActionEvent();
            actionEvent.pos = pos;
            actionEvent.isUp = isUp;
        }
    }

    public void ExeEvent()
    {
        if (actionEvent != null)
        {
            if(actionEvent.isKey)
            {

            }
            else
            {
                MouseOnClickEvent(actionEvent.pos, actionEvent.isUp);
            }
            actionEvent = null;
        }
    }

    public Vector2 MouseButtonEvent(Vector2 pos)
    {
        float scaleX = rawImage.transform.localScale.x;
        float scaleY = rawImage.transform.localScale.y;
        float x = 0, y = 0;
        float offtn = widthH - (width / 2);
        //if (pos.x> widthH)
        //{
        //    x = pos.x - offtn / scaleX;
        //}
        //else
        //{
        //    x = pos.x - offtn * scaleX;
        //}
        //if (pos.y > heightH)
        //{
        //    y = pos.y - heightH + (height / 2) / scaleY;
        //}
        //else
        //{
        //    y = pos.y - heightH + (height / 2) * scaleY;
        //}

        x = (pos.x - widthH + (width / 2)) * scaleX;
        y = (pos.y - heightH + (height / 2)) * scaleY;
       return new Vector2( x, height - y);
    }

    private void OnApplicationQuit()
    {
        foreach (var file in arms.files.Values)
        {
            if (file != null)
                file.Close();
        }
        if(fs!=null)
            fs.Close();
        if (tr != null)
            tr.Close();
        //if(sw!=null)
        //    sw.Close();
       
       
    }

    public string path;
    public FileStream fs;
    TextWriter sw;
    TextReader tr;

    public void SeveFileStream()
    {
        if(File.Exists(path))
        {
            File.Delete(path);
        }
        fs = new FileStream(path, FileMode.Append); //初始化文件流
        sw = new StreamWriter(fs);                  //子类是父类
    }

    public void RandFileStream()
    {
        if (!File.Exists(path))
        {
            return;
        }
        tr = File.OpenText(path);   //读取路径
    }

    public void SeveRe(uint[] v)
    {
        if (sw == null) return;
        for(int i=0;i<v.Length;i++)
        sw.WriteLine(v[i]);                  //写入一行数据
                                                 //关闭流
    }

    public void RandRe(uint[] v)
    {
        if (tr == null) return;
        for (int i = 0; i < v.Length; i++)
        {
            string str = tr.ReadLine();            //读取一行数据
            if(i==15)
            {
                if ((v[i]+4).ToString() != str)
                {

                }
            }
            else
            if(v[i].ToString()!=str)
            {

            }
        }
       
        
    }

    public enum AnonymousEnum9
    {
        MR_KEY_0, //按键 0
        MR_KEY_1, //按键 1
        MR_KEY_2, //按键 2
        MR_KEY_3, //按键 3
        MR_KEY_4, //按键 4
        MR_KEY_5, //按键 5
        MR_KEY_6, //按键 6
        MR_KEY_7, //按键 7
        MR_KEY_8, //按键 8
        MR_KEY_9, //按键 9
        MR_KEY_STAR, //按键 *
        MR_KEY_POUND, //按键 #
        MR_KEY_UP, //按键 上
        MR_KEY_DOWN, //按键 下
        MR_KEY_LEFT, //按键 左
        MR_KEY_RIGHT, //按键 右
        MR_KEY_POWER, //按键 挂机键
        MR_KEY_SOFTLEFT, //按键 左软键
        MR_KEY_SOFTRIGHT, //按键 右软键
        MR_KEY_SEND, //按键 接听键
        MR_KEY_SELECT, //按键 确认/选择（若方向键中间有确认键，建议设为该键）
        MR_KEY_VOLUME_UP, //按键 侧键上
        MR_KEY_VOLUME_DOWN, //按键 侧键下
        MR_KEY_CLEAR,
        MR_KEY_A, //游戏模拟器A键
        MR_KEY_B, //游戏模拟器B键
        MR_KEY_CAPTURE, //拍照键
        MR_KEY_NONE //按键 保留
    }

    //C++ TO C# CONVERTER CRACKED BY X-CRACKER 2017 NOTE: Enums must be named in C#, so the following enum has been named AnonymousEnum10:
    public enum AnonymousEnum10
    {
        MR_KEY_PRESS, //0
        MR_KEY_RELEASE, //1
        MR_MOUSE_DOWN, //2
        MR_MOUSE_UP, //3
        MR_MENU_SELECT, //4
        MR_MENU_RETURN, //5
        MR_DIALOG_EVENT, //6
        MR_SMS_INDICATION, //7
        MR_EVENT_EXIT, //8
        MR_EXIT_EVENT = 8, //8
        MR_SMS_RESULT, //9
        MR_LOCALUI_EVENT, //10
        MR_OSD_EVENT, //11
        MR_MOUSE_MOVE, //12
        MR_ERROR_EVENT, //13执行异常通过这个事件来通知
        MR_PHB_EVENT,
        MR_SMS_OP_EVENT,
        MR_SMS_GET_SC,
        MR_DATA_ACCOUNT_EVENT,
        MR_MOTION_EVENT
    }

    enum state{
        MR_STATE_IDLE,
        MR_STATE_RUN,
        MR_STATE_PAUSE,
        MR_STATE_RESTART,
        MR_STATE_STOP,
        MR_STATE_ERROR
    };

    internal void StopAudio(uint index)
    {
        MidiPlayer.Stop();
    }
}

public class Timer
{
    public int id;
    public float time=-1;
}

public class ActionEvent
{
    public bool isKey;
    public Vector2 pos;
    public bool isUp;
}